Skip to content

Add Dark Mode support for Windows#6666

Draft
AufarZakiev wants to merge 1 commit intoBOINC:masterfrom
AufarZakiev:feature/dark-mode-win
Draft

Add Dark Mode support for Windows#6666
AufarZakiev wants to merge 1 commit intoBOINC:masterfrom
AufarZakiev:feature/dark-mode-win

Conversation

@AufarZakiev
Copy link
Contributor

@AufarZakiev AufarZakiev commented Nov 13, 2025

Fixes #5274

Description of the Change

  1. Bumps WxWidgets version, we need higher than 3.3.1
  2. Forces common elements to use OS theme
  3. Retrieves dark theme flag and carefully repaints
  • Charts (Statistics, Disk panels)
  • Progress bars (to make progress values easily visible)
  • Notices list
  1. Progress column alignment changed to center to follow progress column alignment in other tabs

Tested on Win 11.

Technical implementation

  • Modified MyEvtHandler::OnPaint() to skip PostDrawProgressBarEvent() in dark mode
  • Implemented two-stage NM_CUSTOMDRAW handling:
    • CDDS_ITEMPREPAINT: Request post-paint notification from ListView
    • CDDS_ITEMPOSTPAINT: Draw progress bars directly on back buffer HDC
  • Used raw Win32 GDI for rendering to avoid wxWidgets double-buffering issues

Alternate Designs
Proper progress bars rendering requires to introduce hardly-maintainable code (see DrawItemProgressBar method). To be honest, it is a legacy code from the moment of creation.

Screenshots:

image image image

Release Notes
Add Dark Mode support for Windows

Update history

  • Initial PR
  • Fixed progress bars disappearing on hover in Dark Mode

@AufarZakiev AufarZakiev marked this pull request as draft November 13, 2025 21:20
@AufarZakiev AufarZakiev force-pushed the feature/dark-mode-win branch from 790880a to d0a5c6f Compare November 13, 2025 21:20
@AufarZakiev
Copy link
Contributor Author

Nice PR number for such a change, btw

@AenBleidd
Copy link
Member

@AufarZakiev,

Note: wxWidgets' IsDark() returns false on Win 11 now even if dark mode is enabled. Therefore, registry reading mechanism was introduced as an additional check

For Windows use IsSystemDark() function instead:
https://docs.wxwidgets.org/latest/classwx_system_appearance.html#ac6c70858bb3e4c35a0b1af42997b6c42

@AufarZakiev
Copy link
Contributor Author

AreAppsDark() fits better, doesn't it?
At least, apps on my desktop react on this value and ignore IsSystemDark()

@AenBleidd
Copy link
Member

AreAppsDark() fits better, doesn't it?

Yes, it looks like this is really better. Good catch!

// wxWidgets' IsDark() returns false on Win 11 now even if dark mode is enabled
// so we need to additionally check the registry for the dark mode setting
m_isDarkMode = m_isDarkMode || IsWindowsInDarkMode();
MSWEnableDarkMode(wxApp::DarkMode_Auto);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you sure this is needed? I do remember when I tested the dark mode, I didn't have this enabled

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did it according to docs. Yes, without it just ignores OS settings

@CharlieFenton
Copy link
Contributor

@AufarZakiev @AenBleidd Please make absolutely sure that all your changes are guarded by #ifdef _WIN32 to prevent creating problems for Mac and Linux.

@CharlieFenton
Copy link
Contributor

@AufarZakiev @AenBleidd Please make absolutely sure that all your changes are guarded by #ifdef _WIN32 to prevent creating problems for Mac and Linux.

or #ifdef __WXMSW__

@AenBleidd
Copy link
Member

@CharlieFenton, of course we will, no worries. This is too far from being finished, and it's too early to talk about this now, but this is definitely something I will keep in mind while doing a review of the code.

@AufarZakiev AufarZakiev force-pushed the feature/dark-mode-win branch 2 times, most recently from 6f72d16 to 83f4cf0 Compare November 14, 2025 17:26
@AufarZakiev AufarZakiev marked this pull request as ready for review November 16, 2025 05:28
@AufarZakiev
Copy link
Contributor Author

@AenBleidd , do you have a chance to review PR?

@AenBleidd
Copy link
Member

I had no chance to test this on devices I have.
Hopefully will be done during this weekend.

@AenBleidd
Copy link
Member

@AufarZakiev, we have a progress bar that has a self-written function to draw itself.
It blinks when in dark mode (see the video).
This is something that need to be fixed, because otherwise very annoying.
I have tested this on Win11 and Win10 (video was recorded on Win11, but on Win10 blinking is even more visible).
After this is fixed, I will retest it including Linux.
https://github.com/user-attachments/assets/0e415283-8840-48e5-8f48-4a841a5ed6be

Copy link
Contributor

@CharlieFenton CharlieFenton Dec 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are you changing all these colors? The contents of the graphs do not need to be changed for dark mode. In my opinion, it is wrong to change them. Furthermore, you have not guarded your changes by #ifdef _WIN32 or #ifdef __WXMSW__ to prevent creating problems for Mac and Linux. This file already had all the changes needed for dark mode. Here is how the light and dark mode appear on the Mac with the existing code:

Statistics tab light mode Statistics tab dark mode

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@CharlieFenton, maybe we still can change these colors to be less bright when in Dark mode?
Personally I think with this change they look (at least on Windows) way more consistent than on macOS:
image

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the screenshot. Now that I've seen it, I feel even more certain that the changes in this PR are wrong. It looks terrible and it is very difficult to see the grid lines. You can do what you want on Windows, but please do not change the appearance on the Mac. The BOINC dark mode has been available on the Mac for quite some time, and people have been happy with it as is.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be better to have the same look and feel on all platforms. Maybe we can adjust the intensity of the grid lines to make them more visible while keeping more dark background?
@davidpanderson, what do you think?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are making wholesale changes affecting all platforms, including Mac and Linux. This is unacceptable. You must take care not to change anything affecting the Mac by guarding your changes with #ifdef _WIN32 or #ifdef __WXMSW__

@davidpanderson
Copy link
Contributor

I agree that, in dark mode, the background of the graphs should be fairly dark
(but if this is a problem on the Mac, the light background is OK)

@CharlieFenton
Copy link
Contributor

I think it is worthwhile to take a step back and look at what Microsoft and Apple say about Dark Mode.

This article from Microsoft says:

Light mode is typically the standard setting for most computers, but lots of users are making the switch to Dark mode for a variety of reasons. Here are some benefits to turning on Dark mode:
Aesthetic preference: A common reason why people prefer Dark mode is because it aligns with their aesthetic better than Light mode. If you prefer darker colors over lighter ones, Dark mode will likely fit your style.
Easier on the eyes: Dark mode is often credited for reducing eyestrain, which is a syndrome that can occur if you look at a bright screen for too long. Turning on Dark mode may also make the letters easier to read.
Better for darker locations: A bright screen in a dark room can be harsh on the eyes and could potentially bother those around you. Using Dark mode in a dimly lit airplane cabin or a shared bedroom will help reduce the brightness of your screen to make it stand out less.
Preserves battery: Does Dark mode save battery life? You bet!

I've seen some websites say Reduce eyestrain rather than Easier on the eyes. I think that is primarily about reducing the glare that comes from black letters on a white background. It's pretty clear to me that Dark mode doesn't necessarily involve inverting all colors. If you scroll down a bit in this section of this microsoft article you can see that a color quite similar to our graph background is unchanged in dark mode.

When I asked Google for Microsoft dark mode programming guidelines it replied:

Microsoft's dark mode guidelines focus on using dark grays for backgrounds, lighter surfaces for importance (elevation), avoiding pure white/saturated colors, ensuring accessibility (contrast, focus indicators)

and

Elevation: Use lighter shades of gray for more important surfaces (like dialogs) and darker grays for less important ones to create depth.
Avoid Pure White: Use dark grays for backgrounds and avoid pure white (#FFFFFF) for text/elements to reduce eye strain in dark mode.
Don't Invert: Don't simply invert colors; design specifically for dark mode for optimal results.
Accessibility: Ensure focus indicators (like blue outlines) remain visible.

Another important consideration I see mentioned in the above articles and various others is accessibility. Contrast among elements must be high enough to help people with impaired vision understand the display. That means the grid lines must be more prominent than the screenshot from this PR. I feel the pale blue background we have in Light Mode also works well in Dark Mode and allows the grid lines and data to be easily visible without causing eye strain or glare.

I am willing to consider using the colors that we settle on for Windows in the statistics tab on the Mac if they are pleasing (aesthetic preference) and satisfy all the other considerations discussed above. But I feel the current proposal does not meet those criteria.

Finally, please remember that BOINC has supported Dark Mode on the Mac since June 2023 and the current implementation has been very well received.

@AenBleidd
Copy link
Member

Finally, please remember that BOINC has supported Dark Mode on the Mac since June 2023 and the current implementation has been very well received.

That doesn't mean that it's perfect and cannot be made better ;)

If you scroll down a bit in this section of this microsoft article you can see that a color quite similar to our graph background is unchanged in dark mode.

True, but the color itself is a little bit more soft and darker in comparison with the color we have in BOINC Manager.

But I do agree. I do not want to say that the set of colors in this PR is the final.
I'm open for a discussion, and we need to find the solution that will be satisfiable for all of us.
Personally I don't like graphs background in macOS version because I find it too bright, however on the real macOS screen they might look differently (we all remember that display calibration of macOS devices is one of the best, and other displays usually show slightly different colors.

This needs to be further discussed to find better colors for the next try.

@AenBleidd
Copy link
Member

@CharlieFenton, some examples of graphics from GitHub I personally like:
image
image
I'm not pushing to use them as a guideline, however I like them because they don't have big areas that are too bright or too dark in comparison with the other surrounding areas.

Again, need to be further discussed.

@AenBleidd
Copy link
Member

@AufarZakiev
Copy link
Contributor Author

AufarZakiev commented Dec 10, 2025

FYI: need more time to deal with progress bars blinking. Still a mystery why it happens and only in the dark mode.

@CharlieFenton
Copy link
Contributor

progress bars blinking

This is only a wild guess, but might it be associated with your addition of the remainderColor, textColor, remainderBrush and / or remainderPen? This doesn't happen on the Mac, but as I wrote before:

on MS Windows, BOINC uses the native list control, but on Mac and Linux it uses wxWidgets' wxGenericListCtrl built from wxWidgets source code.

You can test whether it blinks when using the generic list control by temporarily changing lines 25-29 in BOINCListCtrl.h.

@AufarZakiev
Copy link
Contributor Author

AufarZakiev commented Dec 31, 2025

This is only a wild guess, but might it be associated with your addition of the remainderColor, textColor, remainderBrush and or remainderPen?

No, reverting these changes do not prevent blinking.

You can test whether it blinks when using the generic list control by temporarily changing lines 25-29 in BOINCListCtrl.h.

Generic list is not available in Win, I guess. At least, I cannot build project enabling it.

@AufarZakiev
Copy link
Contributor Author

AufarZakiev commented Jan 9, 2026

WxWidgets' developers directly say that

In dark mode, the whole list is custom draw

So it is a little hacky from the very beginning. Now they are fixing numerous bugs in dark mode. For example, master branch already has a fix for proper selected row highlight. But our problem is deeper, since BOINC uses a little hacky "post-draw" method to draw progress bars. Using master branch of WxWidgets does not fix it

Option 1. I can dig even deeper to find root cause in WxWidgets' dark mode implementation. After that, root cause maybe fixed either in WxWidgets or I will come up with another progress bars drawing method specifically for Win dark mode if it possible.
Option 2. Progress bars could be drawn entirely differently, without post-draw events handling. This could be a good option if progress bars render code is legacy. A good moment to rewrite it.
Option 3. Give up and wait for WxWidgets to be fixed internally.

I think, we should try Option 1. @AenBleidd , do you agree?

@AenBleidd
Copy link
Member

@AufarZakiev, yeah, I agree. Option 1 is preferred.
Thank you for your detailed research.

@AufarZakiev AufarZakiev force-pushed the feature/dark-mode-win branch from 83f4cf0 to 9c93ae4 Compare February 14, 2026 20:08
@AufarZakiev
Copy link
Contributor Author

AufarZakiev commented Feb 14, 2026

@AenBleidd , fix is finally there. I updated the initial PR message
The root issue and fix was found by Claude Opus 4.6. It works but the code is hardly maintainable (by humans, ofc).

I asked for very comprehensive comments, they are really good. Here is a main problem description, it is also in the code itself.

WxWidgets' Dark mode is a nightmare
// WHY THIS IS NEEDED:
//
// BOINC's list views (Tasks, Transfers, Projects) show progress bars
// inside a wxListCtrl column. These aren't native progress bar controls --
// they are custom-drawn rectangles with text on top, painted by
// DrawProgressBars() above.
//
// In light mode, the rendering flow works like this:
//
//   1. Windows sends WM_PAINT to the ListView.
//   2. wxWidgets' MyEvtHandler::OnPaint() fires (we pushed it onto the
//      event handler chain in the constructor). It calls event.Skip()
//      to let the native ListView paint itself, then posts a custom
//      wxEVT_DRAW_PROGRESSBAR event via PostDrawProgressBarEvent().
//   3. That posted event is processed after WM_PAINT completes, calling
//      DrawProgressBars(), which uses wxClientDC to draw directly onto
//      the screen surface -- on top of whatever the ListView just painted.
//
// This works fine in light mode because the native Win32 ListView paints
// directly to the screen, so our wxClientDC drawing persists until the
// next WM_PAINT.
//
// In dark mode, wxWidgets enables full owner-draw rendering for the
// ListView via MSWEnableDarkMode(). This changes the painting pipeline:
//
//   1. wxWidgets intercepts NM_CUSTOMDRAW notifications from the ListView.
//   2. For each item, wxWidgets' HandleItemPaint() calls FillRect() to
//      paint the entire row background, draws the item text, and returns
//      CDRF_SKIPDEFAULT to suppress the native theme rendering.
//   3. Critically, all of this drawing happens on a BACK BUFFER HDC --
//      the ListView uses double-buffering (LVS_EX_DOUBLEBUFFER) to
//      eliminate flicker. The back buffer is blitted to the screen only
//      after the entire NM_CUSTOMDRAW cycle completes.
//
// This breaks the old approach: our wxClientDC draws onto the screen
// surface AFTER WM_PAINT, but the back buffer doesn't contain our
// progress bars. The next time the ListView needs to repaint (e.g. on
// hover, which triggers hot-tracking animation via the Explorer theme),
// it blits its back buffer to screen -- erasing our progress bars.
// On hover this happens rapidly, making progress bars flicker or vanish.

I personally do not like adding basically a legacy code (nobody will ever touch it, really). However, I see no other way till the UI is coupled to WxWidgets.

@AufarZakiev
Copy link
Contributor Author

I wonder is it too optimistic to research for a modern multi-platform UI ? For more web-like experience, as it is mentioned in roadmap.
Such huge changes is a place where the AI shines.

@AenBleidd
Copy link
Member

@AufarZakiev, thank you for the commit. I'll try to review it when I have time.
Regarding the new framework, at the moment our Manager is small (memory and resources), and it rarely touched.
New web-based GUI would be interesting to see, but it will definitely be more huge, and I'm not sure it will bring any significant advantage. Also, we do not currently have resources for its development and testing.

@CharlieFenton
Copy link
Contributor

@AufarZakiev Your changes to CBOINCListCtrl::DrawProgressBars() are modifying code that is used by Mac and Linux. This is absolutely unacceptable! I don't have the time to constantly review your code. Please do not let this happen again.

@CharlieFenton
Copy link
Contributor

@AufarZakiev Your changes to CBOINCListCtrl::DrawProgressBars() are modifying code that is used by Mac and Linux.

@AufarZakiev I only looked at your changes long enough to find one instance of unguarded changes which may break Mac and Linux. Please make sure all your changes are guarded by #ifdef and affect only Wiindows. Never replace existing code.

@davidpanderson
Copy link
Contributor

@AufarZakiev is trying to help. Let's be encouraging and constructive.

@CharlieFenton
Copy link
Contributor

@AufarZakiev One correction / clarification: please guard your code with #if USE_NATIVE_LISTCONTROL rather than #ifdef _WIN32 or #ifdef __WXMSW__. The Native list control mostly works on the Mac (and probably on Linux, though I don't have an easy way to test that), but is inefficient. If wxWodgets improves it enough in the future, we may want to switch to using the native meson in the future. Using #if USE_NATIVE_LISTCONTROL leaves that option open.

@AufarZakiev AufarZakiev force-pushed the feature/dark-mode-win branch from 9c93ae4 to 1f947b4 Compare February 17, 2026 16:10
@AufarZakiev
Copy link
Contributor Author

AufarZakiev commented Feb 17, 2026

Disclaimer: This comment was drafted with the help of Claude Code to maintain clear English and save everyone's time reading it. The content and opinions are mine.

Hi @CharlieFenton,

Thank you for your vigilance in protecting the Mac and Linux builds. I want to address your concerns directly


Changes that are strictly Windows-only

The vast majority of this PR is guarded by platform checks and has zero effect on Mac or Linux:

  • BOINCGUIApp.cppAreAppsDark() and MSWEnableDarkMode() are inside #if defined(__WXMSW__) (these are MSW-specific wxWidgets APIs, not list control related)
  • BOINCListCtrl.cpp/.h — All new code (MSWOnNotify, HandleDarkModeCustomDraw, DrawItemProgressBar, the PostDrawProgressBarEvent skip in dark mode) is inside #if USE_NATIVE_LISTCONTROL, following your recommendation to use this guard rather than #ifdef __WXMSW__ so the option remains open for future use on other platforms
  • BOINCGUIApp.h — Adds __WXMSW__ to SUPPORTDARKMODE; Mac and GTK were already included, so no change for them
  • NoticeListCtrl.cpp — Removes a Windows-specific IsSystemDark() fallback that was always false on Mac/Linux anyway; no behavioral change on other platforms

Changes that do affect all platforms

There are a few small cross-platform changes I'd like to discuss openly:

1. ViewWork.cpp — Progress column alignment

Progress column header alignment changed from wxLIST_FORMAT_RIGHT to wxLIST_FORMAT_CENTRE. This is a minor cosmetic tweak affecting the column header text. Would you be OK with this, or would you prefer I guard it?

2. ViewResources.cpp — Bug fix + refactor

Fixed a copy-paste bug: m_pieCtrlTotal->SetLabelColour(...) was being called instead of m_pieCtrlBOINC->SetLabelColour(...) when setting up the BOINC disk usage pie chart. This meant the BOINC pie chart's label color was never explicitly set. Also refactored isDarkMode from a local variable to a m_isDarkTheme member. The dark-mode color values themselves are unchanged.

3. wxPieCtrl.cpp/.h — Refactor only

Extracted theme initialization into an ApplyTheme() method. Same logic, same values — purely a refactor to make it callable after construction.


Question on Statistics graph colors (ViewStatistics.cpp)

The color scheme is now split into separate if (isDarkMode) / else blocks. The light-mode values are identical to what was there before, so Mac/Linux in light mode are unaffected.

For dark mode, the colors are adjusted (e.g., darker axis background, brighter graph lines for better contrast on dark backgrounds). I understand your concern about changing the Mac dark-mode appearance. I will be changing these colors on Windows regardless — would you prefer I guard the dark-mode palette with #ifdef __WXMSW__ so Mac keeps its current colors?


A note on workflow

I notice you've assigned yourself as a reviewer and assignee on this PR. I appreciate the attention, but this PR is still a work in progress — I have not requested a review yet. You've mentioned several times that you don't have time to constantly review my code, and I completely understand that. To save us both time and frustration, I'd suggest removing the self-assignment for now and waiting until I explicitly request your review. That way you won't be reviewing intermediate states that are still changing, and when I do ask for your review, I'll be able to confirm that all platform guards are in place and the PR is ready for your attention.


Looking forward to your feedback.

@AufarZakiev
Copy link
Contributor Author

I will also deeply appreciate your feedback on a new BOINC manager called Fresco. I developed it as an experiment and it is pretty good. There is an announcement: #6866

I want to express my admiration on how good manager and client are separated. It was a real pleasure to know how easy is to make a replacement for a manager without touching any of client's code. Magnificent!

@CharlieFenton
Copy link
Contributor

Thank you for your vigilance in protecting the Mac and Linux builds. I want to address your concerns directly

@AufarZakiev Thank you of the detailed response and for your commitment to exercising care not to break the Mac and Linux functionality. As you suggest, i will wait until you let us know your changes aer ready for review before doing anything further.

But I do want to clarify that this comment was in response to your changes to CBOINCListCtrl::DrawProgressBars(). Please correct me if I am mistaken, but your changes throughout this routine are not guarded, specifically your changes to the original lines 527 and, 609-610.

While these do seem to preserve the progress bars' appearance on the Mac in light mode, they substantially change it in dark mode. I don't know what the standards are for dark mode on Windows, but on the Mac dark mode does not mean changing everything to shades of gray, as your changes did with the progress bars. I spent some time selecting a progress bar color for dark mode on the Mac and Linux and I prefer that not be changed on these platforms.

Progress column header alignment changed from wxLIST_FORMAT_RIGHT to wxLIST_FORMAT_CENTRE.

I don't have a strong feeling either way. On the one hand, you have made it consistent with the header for that column in the Projects and Transfers tabs; on the other hand, it is inconsistent (in all three tabs) with all the other headers which are either right justified or left justified.

@AufarZakiev
Copy link
Contributor Author

Yes, you are right, DrawProgressBars() affects Mac colors.

Ok, summary is:

  • Do not change colors for Mac at all (both progress bars and charts)
  • Change Progress bar alignment to wxLIST_FORMAT_CENTRE to keep it at least consistent with other progress columns

Other changes are refactoring / bugfixes / Windows-only. Is it OK with you, @CharlieFenton ?

@CharlieFenton
Copy link
Contributor

Is it OK with you, @CharlieFenton ?

Yes, I think so, but I will again review everything when you say you are ready for me to do that. Thank you.

@AufarZakiev AufarZakiev force-pushed the feature/dark-mode-win branch from 1f947b4 to 9043956 Compare March 2, 2026 20:08
@AufarZakiev
Copy link
Contributor Author

@CharlieFenton @AenBleidd you are welcome to review

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: Review in progress

Development

Successfully merging this pull request may close these issues.

[Manager] Add dark mode support on Windows

4 participants